﻿using System;
using System.Text;
namespace sumath.core
{
    /// 简单数变量, 统一数
    public struct sunum
    {
        public static readonly sunum positive_infinity = new sunum { data_size = -1, dir = 0 }; // 正无穷
        public static readonly sunum negative_infinity = new sunum { data_size = -1, dir = 1 }; // 负无穷
        public static readonly sunum error = new sunum { data_size = -99 }; // 计算错误

        internal byte[] data;
        internal byte[] decimal_data; //小数存储
        internal int data_size;
        internal byte dir; // 0 正向  1 负向
        internal int decimal_size; // 小数位数
        public sunum()
        {
            data = new byte[0];
            decimal_data = new byte[0];
            data_size = 0;
            dir = 0;
            decimal_size = 0;
        }
        public sunum(int size, int decimal_size = 0)
        {
            data = new byte[size];
            decimal_data = new byte[decimal_size];
            data_size = size;
            dir = 0;
            this.decimal_size = decimal_size;
        }

        public void setdir(bool zf)
        {
            if (zf) dir = 0;
            else dir = 1;
        }

        public static sunum Parse(object data)
        {
            sunum num;
            switch (data)
            {
                case byte _byte:
                    num = new sunum(1);
                    num.data[0] = _byte;
                    return num;
                case short _short:
                    num = new sunum(2);
                    num.setdir((_short & short.MinValue) == 0);
                    if (num.dir == 1) _short = (short)-_short;

                    num.data[0] = (byte)(_short >> 8);
                    num.data[1] = (byte)(_short);
                    return num;
                case ushort _ushort:
                    num = new sunum(2);
                    num.data[0] = (byte)(_ushort >> 8);
                    num.data[1] = (byte)(_ushort);
                    return num;
                case int _int:
                    num = new sunum(4);
                    num.setdir((_int & int.MinValue) == 0);
                    if (num.dir == 1) _int = -_int;
                    
                    num.data[0] = (byte)(_int >> 24);
                    num.data[1] = (byte)(_int >> 16);
                    num.data[2] = (byte)(_int >> 8);
                    num.data[3] = (byte)(_int);
                    return num;
                case uint _uint:
                    num = new sunum(4);
                    num.data[0] = (byte)(_uint >> 24);
                    num.data[1] = (byte)(_uint >> 16);
                    num.data[2] = (byte)(_uint >> 8);
                    num.data[3] = (byte)(_uint);
                    return num;
                case long _long:
                    num = new sunum(8);
                    num.setdir((_long & long.MinValue) == 0);
                    if (num.dir == 1) _long = -_long;

                    num.data[0] = (byte)(_long >> 56);
                    num.data[1] = (byte)(_long >> 48);
                    num.data[2] = (byte)(_long >> 40);
                    num.data[3] = (byte)(_long >> 32);
                    num.data[4] = (byte)(_long >> 24);
                    num.data[5] = (byte)(_long >> 16);
                    num.data[6] = (byte)(_long >> 8);
                    num.data[7] = (byte)(_long);
                    return num;
                case ulong _ulong:
                    num = new sunum(8);
                    num.data[0] = (byte)(_ulong >> 56);
                    num.data[1] = (byte)(_ulong >> 48);
                    num.data[2] = (byte)(_ulong >> 40);
                    num.data[3] = (byte)(_ulong >> 32);
                    num.data[4] = (byte)(_ulong >> 24);
                    num.data[5] = (byte)(_ulong >> 16);
                    num.data[6] = (byte)(_ulong >> 8);
                    num.data[7] = (byte)(_ulong);
                    return num;
                case float _float:
                    num = new sunum(8);
                    num.setdir(_float < 0);

                    float integerPart = (float)Math.Truncate(_float);
                    float fractionalPart = _float - integerPart;
                    break;
                case double _double:
                    num = new sunum(8);
                    num.setdir(_double < 0);
                    var integerPart_double = Math.Truncate(_double);
                    var fractionalPart_double = _double - integerPart_double;
                    break;
                case decimal _decimal: // 128 字节
                    num = new sunum(128);
                    num.setdir(_decimal < 0);
                    var integerPart_decimal = Math.Truncate(_decimal);
                    var fractionalPart_decimal = _decimal - integerPart_decimal;
                    break;
                case string _text:
                    if (_text == "∞") return positive_infinity;
                    if (_text == "-∞") return negative_infinity;
                    num = new sunum();
                    num.data = DecimalToBinary(_text);
                    num.data_size = num.data.Length;
                    return num;
            }

            return error;
        }

        public override string ToString()
        {
            if (data_size == -99) return "ERR";
            var _out = new StringBuilder();
            if (dir == 1) _out.Append("-");
            if (data_size == -1) _out.Append("∞");
            else _out.Append(BinaryToDecimal(data, 0, data_size));

            if (decimal_size > 0)
            {
                _out.Append('.');
            }
            return _out.ToString();
        }



        internal static byte[] DecimalToBinary(string decimalStr)
        {
            if (decimalStr == "0") return new byte[1] { 0 };

            // 转换为字符数组并去除前导零
            char[] digits = decimalStr.TrimStart('0').ToCharArray();
            int length = digits.Length;
            char[] binary = new char[length * 4]; // 预分配空间（二进制位数约为十进制的3.32倍）
            int binaryIndex = 0;

            while (length > 0)
            {
                int remainder = 0;
                for (int i = 0; i < length; i++)
                {
                    int temp = remainder * 10 + (digits[i] - '0');
                    digits[i] = (char)(temp / 2 + '0');
                    remainder = temp % 2;
                }

                binary[binaryIndex++] = remainder.ToString()[0]; // 记录余数
                                                                 // 去除前导零
                int newLength = 0;
                while (newLength < length && digits[newLength] == '0') newLength++;
                if (newLength == length)
                {
                    length = 0;
                }
                else
                {
                    Array.Copy(digits, newLength, digits, 0, length - newLength);
                    length -= newLength;
                }
            }

            // 反转二进制数组并去除前导零
            Array.Reverse(binary, 0, binaryIndex);
            int start = 0;
            while (start < binaryIndex && binary[start] == '0') start++;
            if (start == binaryIndex) return new byte[1] { 0 };
            var binary_size = (binaryIndex - start);
            var pos = (binary_size % 8);
            var size = binary_size / 8 + (pos > 0 ? 1 : 0);

            var buffer = new byte[size];
            pos--;
            byte raw = 0;
            int bufferpos = 0;

            if (pos < 0) pos = 7;
            for (var i = start; i < binaryIndex; i++)
            {
                if (binary[i] == '1') raw |= (byte)(1 << pos);
                pos--;
                if (pos < 0)
                {
                    buffer[bufferpos++] = raw;
                    pos = 7;
                    raw = 0;
                }
            }

            return buffer;
        }

        static string BinaryToDecimal(byte[] binary, int offset, int length)
        {
            string decimalStr = "0";
            length = offset + length;
            int i;
            for (int pos = offset; pos< length; pos++)
            {
                for (int j = 7; j >= 0; j--)
                {
                    char[] digits = decimalStr.ToCharArray();
                    int carry = 0;
                    for (i = digits.Length - 1; i >= 0; i--)
                    {
                        int product = (digits[i] - '0') * 2 + carry;
                        digits[i] = (char)(product % 10 + '0');
                        carry = product / 10;
                    }
                    if (carry > 0) decimalStr = carry.ToString() + new string(digits);
                    else decimalStr = new string(digits);

                    if (((binary[pos] >> j) & 0b1) == 1)
                    {
                        digits = decimalStr.ToCharArray();
                        carry = 1;
                        for (i = digits.Length - 1; i >= 0; i--)
                        {
                            int sum = (digits[i] - '0') + carry;
                            digits[i] = (char)(sum % 10 + '0');
                            carry = sum / 10;
                            if (carry == 0) break;
                        }
                        if (carry > 0) decimalStr = "1" + new string(digits);
                        else decimalStr = new string(digits);
                    }
                }
            }
            return decimalStr;
        }
    }
}
